package xdu.lz.netty.c_nio_buffer_channel;


import org.junit.Test;

import java.io.RandomAccessFile;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.MappedByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.FileLock;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.Arrays;

public class NioDerictBuffer {

    /**
     * 1 内存映射文件：
     *      通过堆外内存映射到IO文件，
     *      修改内存即可，OS会自动更新到文件，不用和IO直接打交道而实现了迅速的IO操作
     *
     */
    @Test
    public void testRandom() throws Exception{
        /**
         * RandomAccessFile是java Io体系中功能最丰富的文件内容访问类。即可以读取文件内容，
         * 也可以向文件中写入内容。但是和其他输入/输入流不同的是，程序可以直接跳到文件的任意位置来读写数据。
         *   因为RandomAccessFile可以自由访问文件的任意位置，所以如果我们希望只访问文件的部分内容，
         * 那就可以使用RandomAccessFile类。与OutputStearm,Writer等输出流不同的是，
         * RandomAccessFile类允许自由定位文件记录指针，所以RandomAccessFile可以不从文件开始的地方进行输出，
         * 所以RandomAccessFile可以向已存在的文件后追加内容。则应该使用RandomAccessFile。
         */
        RandomAccessFile randomAccessFile = new RandomAccessFile("NioTest4.txt", "rw");
        FileChannel fileChannel = randomAccessFile.getChannel();

        /**
         * @param 1 模式 ： 读，写 ，读和写
         * @param 2 3  读取文件的字节的起始和终止位置
         */
        MappedByteBuffer mappedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, 5);

        //直接在内存中修改数据，内存直接映射到文件
        mappedByteBuffer.put(0, (byte)'a');
        mappedByteBuffer.put(3, (byte)'b');

        randomAccessFile.close();
    }
    /**
     * 2设置锁，用的不多
     */
    @Test
    public void test2Lock() throws Exception{
        RandomAccessFile randomAccessFile = new RandomAccessFile("NioTest10.txt", "rw");
        FileChannel fileChannel = randomAccessFile.getChannel();

        FileLock fileLock = fileChannel.lock(3, 6, true);

        System.out.println("valid: " + fileLock.isValid());
        System.out.println("lock type: " + fileLock.isShared());

        fileLock.release();

        randomAccessFile.close();
    }
    /**
     * 3关于Buffer的Scattering与Gathering。  可以有多个buffer
     *   Scattering把读取的消息分散地放在每个buffer中，注意一定是前一个buffer满了才会自动读取到下一个buffer中。
     *   Gathering是把消息写出去，分别从buffer数组写，第一个写完来才会写第二个
     *   应用：如http，第一个buffer接受头，第二个接受消息体，天然把数据分开不用再手动分开
     */
    @Test
    public void test3() throws  Exception{
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        InetSocketAddress address = new InetSocketAddress(8899);
        serverSocketChannel.socket().bind(address);

        int messageLength = 2 + 3 + 4;

        ByteBuffer[] buffers = new ByteBuffer[3];

        buffers[0] = ByteBuffer.allocate(2);
        buffers[1] = ByteBuffer.allocate(3);
        buffers[2] = ByteBuffer.allocate(4);

        SocketChannel socketChannel = serverSocketChannel.accept();

        while (true) {
            int bytesRead = 0;

            while (bytesRead < messageLength) {
                long r = socketChannel.read(buffers);
                bytesRead += r;

                System.out.println("bytesRead: " + bytesRead);

                for (int i = 0; i < buffers.length; i++) {
                    System.out.println("buffer:"+buffers[i]);
                }
//                Arrays.asList(buffers).stream().
//                        map(buffer -> "position: " + buffer.position() + ", limit: " + buffer.limit()).
//                        forEach(System.out::println);
            }

            Arrays.asList(buffers).forEach(buffer -> {
                buffer.flip();
            });

            long bytesWritten = 0;
            while (bytesWritten < messageLength) {
                long r = socketChannel.write(buffers);
                bytesWritten += r;
            }

            Arrays.asList(buffers).forEach(buffer -> {
                buffer.clear();
            });

            System.out.println("bytesRead: " + bytesRead + ", bytesWritten: "
                    + bytesWritten + ", messageLength: " + messageLength);
        }
    }
}
